#define _SCL_SECURE_NO_WARNINGS

#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include "XmlUtils.h"
#include "Exception.h"
#include "Logger.h"

using namespace boost;
using namespace game_engine::data_format;
using namespace game_engine::utils;

string XmlUtils::SEPARATOR = ".";

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

XmlUtils::~XmlUtils(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

//XmlNode* XmlUtils::parseFile(const string &filename, XmlDocument &doc)
//{
//    // Read all content from input file and store it into a single string
//    string fileContent = readAll(filename);
//
//    // Parse the contents
//    XmlDocument d;
//    d.parse<0>(const_cast<char *>(fileContent.c_str()));
//
//    XmlNode* rootNode = doc.clone_node(&d);
//    return rootNode;
//}

const list< XmlNode* > XmlUtils::getNodes(const string &path, XmlDocument &doc)
{
    list< XmlNode* > nodes;

    // Split input path
    list<string> nodeList = splitPath(path);

    // Find first node
    XmlNode *node = findNode(nodeList, doc);

    while (node != NULL)
    {
        nodes.push_back(node);
        node = node->next_sibling((nodeList.back()).c_str());
    }

    return nodes;
}

const INT32 XmlUtils::getINT32(const string &path, XmlDocument &doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return static_cast<INT32>(atoi(node->value()));
}

const UINT32 XmlUtils::getUINT32(const string &path, XmlDocument &doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return static_cast<UINT32>(atoi(node->value()));
}

const FLOAT32 XmlUtils::getFLOAT32(const string &path, XmlDocument &doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return static_cast<FLOAT32>(atof(node->value()));
}

const string XmlUtils::getString(const string &path, XmlDocument &doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return string(node->value());
}

const INT32 XmlUtils::getINT32(const string &path, XmlNode *rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return static_cast<INT32>(atoi(node->value()));
}

const UINT32 XmlUtils::getUINT32(const string &path, XmlNode *rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return static_cast<UINT32>(atoi(node->value()));
}

const FLOAT32 XmlUtils::getFLOAT32(const string &path, XmlNode *rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return static_cast<FLOAT32>(atof(node->value()));
}

const string XmlUtils::getString(const string &path, XmlNode *rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return string(node->value());
}

const string XmlUtils::readAll(const string &filename)
{
    // Read all contents from the file
    ifstream fin(filename.c_str(), ifstream::in);
    if (!fin || !fin.good() || fin.eof())
    {
        // throw exception
        string err("error reading input file");
        throw runtime_error(err);
    }

    string content;
    while (!fin.eof())
    {
        string line;
        getline(fin, line);
        content += line + '\n';
    }

    return content;
}

///////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////

const list<string> XmlUtils::splitPath(const string &path)
{
    // Split input path using SEPARATOR
    list<string> nodeList;
    split(nodeList, path, is_any_of(SEPARATOR));
    if (nodeList.empty())
    {
        string msg = "Parsing value failed : invalid path";
        Logger::ERR(GAMEENGINE_LOG, msg);
        throw ParserException(msg);
    }
    
    return nodeList;
}

XmlNode* XmlUtils::findNode(list<string> &nodeList, XmlDocument &doc)
{
    // Iterate over all nodes
    XmlNode *rootNode = doc.first_node((nodeList.front()).c_str());
    nodeList.pop_front();
    XmlNode *node = rootNode;
    BOOST_FOREACH(string nodeName, nodeList)
    {
        node = node->first_node(nodeName.c_str());
        if (node == NULL)
        {
            string msg = "Parsing value failed : invalid node name " + nodeName;
            Logger::ERR(GAMEENGINE_LOG, msg);
            throw ParserException(msg);
        }
    }

    return node;
}

XmlNode* XmlUtils::findNode(const string &path, XmlDocument &doc)
{
    // Split input path
    list<string> nodeList = XmlUtils::splitPath(path);

    // Iterate over all all nodes
    XmlNode *rootNode = doc.first_node("level_parameters");
    if (rootNode == NULL)
    {
        string msg = "Parsing value failed : invalid node name " 
            + nodeList.front();
        Logger::ERR(GAMEENGINE_LOG, msg);
        throw ParserException(msg);
    }
    nodeList.pop_front();

    return getNode(nodeList, rootNode);
}

XmlNode* XmlUtils::getNode(const list<string> &nodeList, XmlNode *rootNode)
{
    XmlNode *node = rootNode;
    BOOST_FOREACH(string nodeName, nodeList)
    {
        node = node->first_node(nodeName.c_str());
        if (node == NULL)
        {
            string msg = "Parsing value failed : invalid node name " + nodeName;
            Logger::ERR(GAMEENGINE_LOG, msg);
            throw ParserException(msg);
        }
    }

    return node;
}
